《windows PE》导出表

第一部分 预备知识

         导入表主要存在于动态链接库文件中,用于将dll文件中的函数导入到外部,给其他的exe或者dll文件调用。我们在导入表一章中知道了程序在装载过程中,通过在INT获得的函数地址覆盖到IAT中,此时,导出表起到了参照和指引的作用。

第二部分 导出表数据结构

1.定位导出表

         导出表数据在数据目录的第一个目录中,定位方法和获取导入表的一样。

2.导出目录IMAGE_EXPORT_DIRECTORY

         第一个结构是导出表描述符,导入表的IMAGE_EXPORT_DIRECTORY只有一个。下面是其详细定义:

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct _IMAGE_EXPORT_DIRECTORY
{
DWORD Characteristics; // 未使用,总为0 DWORD TimeDateStamp; // 文件创建时间戳
WORD MajorVersion; // 未使用,总为0 WORD MinorVersion; // 未使用,总为0
DWORD Name; // 指向一个代表此 DLL名字的 ASCII字符串的 RVA
DWORD Base; // 函数的起始序号
DWORD NumberOfFunctions; // 导出函数的总数
DWORD NumberOfNames; // 以名称方式导出的函数的总数
DWORD AddressOfFunctions; // 指向输出函数地址的RVA
DWORD AddressOfNames; // 指向输出函数名字的RVA
DWORD AddressOfNameOrdinals; // 指向输出函数序号的RVA
}IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

  • Name:文件最初的文件名
  • NumberOfFunctions:导出函数的个数
  • NumberOfNmaes:导出函数其中有名字的个数,NumberOfNames的值小于NumberOfFunctions
  • AddressOfFunctions:该指针指向的是所有导出函数的入口地址的起始。函数地址顺序按照函数的编号排序。
  • Base:导出函数编号得起始值,第一个导出函数的编号不是从0开始的,某个导出函数的编号等于base+AddressOfFunctions的所在编号。
  • AddressOfName:该值是一个指针,指向的位置是连续的双字节,这些双字节指向的是函数名字符串的地址。
  • AddressOfNameOrdinals:指向的是函数数字编号得地址。

3.导出表的应用

         导出表常见的主要应用是对导出表函数的覆盖和对dll文件内部函数的导出

3.1.导出函数覆盖

  • 修改导出表里面函数的地址。
  • 覆盖函数地址部分函数代码。

3.2.导出私有函数

  • 获取导出函数地址时
    • 在AddressOfNames中找到对应的名字,比如Func2,他在AddressOfNames中是第二项
    • 然后从AddressOfNameOrdinals中取出第二项的值,这里是2,表示函数入口保存在AddressOfFunctions这个数组中下标为2的项里,
    • 取出AddressOfFunctions里面对应AddressOfNameOrdinals的索引值。
    • 加上模块基地址便是导出函数的地址。

      https://blog.csdn.net/evileagle/article/details/12176797
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      DWORD* CEAT::SearchEAT( const char* szName)
      {
      if (IS_VALID_PTR(m_pTable))
      {
      bool bByOrdinal = HIWORD(szName) == 0;
      DWORD* pProcs = (DWORD*)((char*)RVA2VA(m_pTable->AddressOfFunctions));
      if (bByOrdinal)
      {
      DWORD dwOrdinal = (DWORD)szName;
      if (dwOrdinal < m_pTable->NumberOfFunctions && dwOrdinal >= m_pTable->Base)
      {
      return &pProcs[dwOrdinal-m_pTable->Base];
      }
      }
      else
      {
      WORD* pOrdinals = (WORD*)((char*)RVA2VA(m_pTable->AddressOfNameOrdinals));
      DWORD* pNames = (DWORD*)((char*)RVA2VA(m_pTable->AddressOfNames));
      for (unsigned int i=0; i<m_pTable->NumberOfNames; ++i)
      {
      char* pNameVA = (char*)RVA2VA(pNames[i]);
      if (strcmp(szName, pNameVA) != 0)
      {
      continue;
      }
      return &pProcs[pOrdinals[i]];
      }
      }
      }
      return NULL;
      }